import rclpy
from rclpy.node import Node
from chapt4_interfaces.srv import FaceDetector
import face_recognition
import cv2
from ament_index_python.packages import get_package_share_directory
import os
from cv_bridge import CvBridge
import time
from rcl_interfaces.srv import SetParameters  #是一个复合消息接口，里面还有一个消息接口
from rcl_interfaces.msg import Parameter #是一个
from rcl_interfaces.msg import ParameterValue,ParameterType

class FaceDetectClientNode(Node):
    def __init__(self):
        super().__init__("face_detect_client_node")
        self.bridge_ = CvBridge()
        self.default_img_path = get_package_share_directory('demo_python_service') + '/resource/test1.jpg'
        self.get_logger().info(f"人脸检测客户端已启动...")
        #第一个参数是服务的类型，第二个参数是服务的名称
        self.client = self.create_client(FaceDetector,"face_detect")
        #OpenCV 的 imread 函数用于从指定文件加载图像，并将其以多维 NumPy 数组的形式返回，这个数组包含了图像的像素信息，可以进行各种图像处理操作
        self.image = cv2.imread(self.default_img_path)

    def call_set_parameters(self,parameters):
        #调用服务，修改参数
        # 1.判断服务端是否在线    第二个参数：face_detect_node/set_parameters，face_detect_node是服务名 ，set_parameters是内置的接口名
        update_param_client = self.create_client(SetParameters,"face_detect_node/set_parameters")
        while update_param_client.wait_for_service(timeout_sec=1.0) is False:
            self.get_logger().info("等待服务端启动...")


        
        # 2.创建一个 SetParameters.Request 请求
        request = SetParameters.Request()
        # 将参数列表添加到请求中
        request.parameters = parameters
        # 3.调用服务端接口，更新参数
        future = update_param_client.call_async(request)
        # 4.等待处理响应结果（因为是更新参数，这个场景下，我们是需要阻塞程序，等待参数更新完然后做下一步处理的）
        rclpy.spin_until_future_complete(self,future)  #阻塞等待future 完成
        response = future.result()
        return response
    
    def update_detect_model(self,model='hog'):
        #更具传入的model，构造parameters，然后调用call_set_parameters 函数，去修改服务端参数
        # 1.创建一个 Parameter 参数对象,, 注意要严格按照：ros2 interface show rcl_interfaces/srv/SetParameters 格式来
        # 注意：ParameterValue() 只有两个参数需要填，一个是param_value.type，param_value.string_value(uint8/bool/float64/string/...)
        param = Parameter()
        param.name = "model" 
        # 2.创建param_value
        param_value = ParameterValue()
        param_value.string_value = model 
        param_value.type = ParameterType.PARAMETER_STRING

        param.value = param_value
        # 3.请求更新参数
        response = self.call_set_parameters([param])
        for result in response.results:
            if result.successful:
                self.get_logger().info(f"参数 {param.name} 更新成功,更新为{model}")
            else:
                self.get_logger().info(f"参数 {param.name} 更新失败,更新为{model}")


    
    def send_request(self):
        # 1.判断服务端是否在线
        while self.client.wait_for_service(timeout_sec=1.0) is False:
            self.get_logger().warn("等待服务端启动...")
        # 2.创建请求
        request = FaceDetector.Request()
        #把本地的cv 图像格式 转换为ros2 格式
        request.image = self.bridge_.cv2_to_imgmsg(self.image)
        future = self.client.call_async(request)

        # { #以下是错误案例
        # # #发送请求并等待处理完成,,ros2 每一个可执行文件，默认都是单线程的 ，如果按照下面那样写，那在接收到response之前程序就卡在这里了，无法接受到新的请求
        # # while not future.done():
        # #     time.sleep(1.0) #休眠当前线程等待service 处理完成 
        # # 
        # }
        #rclpy.spin_until_future_complete(self,future)  #阻塞等待future 完成，spin_until_future_complete 是阻塞的，会导致spin 无法正常运行。
        
        def result_callback(result_future):
            response = result_future.result(); #获取响应
            self.get_logger().info(f"接收到的响应:{response.number}张人脸，耗时{response.use_time}s")
            self.show_response(response)

        future.add_done_callback(result_callback) #添加回调函数，当future 完成时，会调用这个回调函数,这样代码 就不阻塞了。
        
        

    def show_response(self,response):
        for i in range(response.number):
            cv2.rectangle(   #对 self.image图像 画图，画矩形框标注人脸
                self.image,
                (response.left[i],response.top[i]),
                (response.right[i],response.bottom[i]),
                (0,0,255),
                4
                ) 
        cv2.imshow("Face Detecte Result",self.image)  #显示 self.image图像
        cv2.waitKey(0)   #也是阻塞的，会导致spin 无法正常运行。

def main():
    rclpy.init()
    node = FaceDetectClientNode()
    node.update_detect_model('cnn')
    node.send_request()
    node.update_detect_model('hog')
    node.send_request()

    node.send_request()
    rclpy.spin(node)   #如果改称异步，future.add_done_callback(result_callback)，那么 要把 node注册到 rclpy.spin(node)
    rclpy.shutdown()